#include "WorkFlow.h"
#include "Plugin.h"
#include "PluginHelper.h"
#include "Context.h"
using namespace yazi::engine;

#include "Document.h"
#include "Element.h"
using namespace yazi::xml;

#include "Singleton.h"
#include "Logger.h"
using namespace yazi::utility;

WorkFlow::WorkFlow(){

}

WorkFlow::~WorkFlow(){
    for(auto it = m_works.begin(); it != m_works.end(); ++it){
        delete it->second;
        it->second = nullptr;
    }
    m_works.clear();
}

bool WorkFlow::load(const string& filename){
    Document doc;
    doc.load_file(filename);
    Element root = doc.parse();
    for(auto it = root.begin(); it != root.end(); ++it){
        const string& name = it->attr("name");
        const string& flag = it->attr("switch");
        if(it->name() != "work"){
            error("workflow::load error, name != work");
            return false;
        }
        if(flag != "on" && flag != "off"){
            error("work switch = %s, invalid switch", flag.c_str());
            return false;
        }
        Work* work = new Work();
        work->set_name(name);
        if(flag == "on") 
            work->set_switch(true);
        else 
            work->set_switch(false);
        if(!load_plugin(work, (*it))){
            delete work;
            return false;
        }
        m_works[name] = work;
        info("load work, name: %s", name.c_str());
    }
    return true;
}

bool WorkFlow::load_plugin(Work* work, Element& element){
    for (auto it = element.begin(); it != element.end(); ++it){
        const string &name = it->attr("name");
        const string &flag = it->attr("switch");
        if (it->name() != "plugin"){
            error("workflow::load_plugin error, name != plugin");
            return false;
        }
        if (flag != "on" && flag != "off"){
            error("work switch = %s, invalid switch", flag.c_str());
            return false;
        }
        create_func func = (create_func)Singleton<PluginHelper>::instance()->get(name, "create");
        if(func == NULL){
            error("load plugin failed, func = NULL");
            return false;
        }
        Plugin* plugin = func();
        plugin->set_name(name);
        if(flag == "on") 
            plugin->set_switch(true);
        else 
            plugin->set_switch(false);
        work->append(plugin);
    }
    return true;
}

bool WorkFlow::run(const string& work, const string& input, string& output){
    auto it = m_works.find(work);
    if(it == m_works.end()){
        error("work %s not found", work.c_str());
        return false;
    }
    if(it->second->get_switch() == false){
        error("work %s switch off", work.c_str());
        return false;
    }
    Context ctx;
    ctx.ref<string>("input") = input;
    if(!it->second->run(ctx)){
        error("work %s run error", work.c_str());
        return false;
    }
    output = ctx.ref<string>("output");
    return true;
}